package xuecheng.tool.aop;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.scheduling.annotation.Async;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import xuecheng.tool.json.JsonUtils;
import xuecheng.tool.json.JsonUtilsFactory;

/**
 * 使用 aop 切面记录方法日志信息
 *
 * @author PengXueCheng
 * @version 1.0.0
 **/
@Aspect
@Slf4j
public class MethodLogAop {

    private JsonUtils jsonUtils = JsonUtilsFactory.obtain();

    /**
     * 切入点
     */
    @Pointcut("@annotation(xuecheng.tool.aop.MethodLog)||@within(xuecheng.tool.aop.MethodLog)")
    public void pointcut() {}

    /**
     * 环绕操作
     *
     * @param point
     *            切入点
     * @return 原方法返回值
     * @throws Throwable
     *             异常信息
     */
    @Around("pointcut()")
    public Object main(ProceedingJoinPoint point) throws Throwable {
        Long start = System.currentTimeMillis();
        // 返回值
        Object result = point.proceed();
        Long end = System.currentTimeMillis();
        // 请求耗时毫秒
        String consumingTimeStr = StrUtil.format("{}ms", end - start);

        // 输出日志
        SpringUtil.getBean(this.getClass()).printLog(point, result, consumingTimeStr);

        return result;
    }

    /**
     * 输出日志
     *
     * @param point
     *            链接点
     * @param result
     *            方法结果
     * @param consumingTimeStr
     *            方法耗时
     */
    @Async
    public void printLog(ProceedingJoinPoint point, Object result, String consumingTimeStr) {
        // 请求方法签名
        String methodSignature = point.getSignature().toLongString();
        // 请求方法参数
        List<String> args = Arrays.stream(Optional.ofNullable(point.getArgs()).orElse(new Object[0]))
            .map(o -> jsonUtils.toJson(o)).collect(Collectors.toList());
        String resultJson = "void";
        if (result != null) {
            resultJson = jsonUtils.toJson(result);
        }

        log.info("|方法执行日志|{}|{}|{}|{}", consumingTimeStr, methodSignature, resultJson, args);
    }

}
